package net.sf.rose.jdbc.database;

import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import net.sf.rose.boot.BootStrap;
import net.sf.rose.boot.bean.DataTypeBean;
import net.sf.rose.boot.bean.DatabaseBean;
import net.sf.rose.jdbc.ConstantCode;
import net.sf.rose.jdbc.bean.FieldBean;
import net.sf.rose.jdbc.bean.TableBean;
import net.sf.rose.jdbc.service.Service;

/**
 * 类说明:Mysql数据库表结构元数据操作的封装类
 * 
 * @author fengjian
 * @since 2016/04/20
 */
public class pgsql extends Jdbc {

	private static Logger LOG = Logger.getLogger(pgsql.class);

	/**
	 * 功能:返回本数据库所有的表与视图
	 * 
	 * @param service 数据库连接
	 * @return 返回所有的表与视图
	 * @throws SQLException 数据库操作异常
	 */
	@Override
	public List<TableBean> getAllDbTables(Service service) throws SQLException {
		// 检查数据库中是否有此表
		DatabaseBean db = BootStrap.getBootBean().getDatabaseBean(service.getCurrentDatabaseName());
		String databaseName = db.getDatabaseName();
		String sql = "SELECT * FROM information_schema.tables WHERE table_schema='public'";
		sql = sql + " and table_catalog ='" + databaseName + "'";
		ResultSet results = service.getConnection().createStatement().executeQuery(sql);
		List<TableBean> list = new ArrayList<>();
		while (results.next()) {
			if (results.getString("TABLE_TYPE").equalsIgnoreCase("VIEW")) {
				continue;
			}
			TableBean t = new TableBean();
			t.setDatabaseName(databaseName);
			t.setTableName(results.getString("TABLE_NAME"));
			list.add(t);
		}
		results.close();
		// 如果表结构存在
		for (TableBean table : list) {
			// 从数据库获取元数据
			Map<String, DataTypeBean> types = service.getDataBaseTypeMap();
			// 获取视图的字段信息
			PreparedStatement pstmt;
			sql = "SELECT * FROM " + table.getTableName();
			List<FieldBean> fields = new ArrayList<>();
			pstmt = service.getConnection().prepareStatement(sql);
			ResultSetMetaData metaData = pstmt.getMetaData();
			for (int i = 0; i < metaData.getColumnCount(); i++) {
				FieldBean field = new FieldBean();
				field.setFieldName(metaData.getColumnName(i + 1).toUpperCase());
				field.setDescription(metaData.getColumnLabel(i + 1));
				field.setDbType(metaData.getColumnTypeName(i + 1));
				DataTypeBean type = types.get(field.getDbType().toUpperCase());
				if (type == null) {
					LOG.error(field.getFieldName() + ':' + field.getDbType() + "没有对应的类型！");
					continue;
				}
				field.setType(type.getName());
				field.setJavaType(type.getJavaType());
				field.setPropertyName(field.getFieldName());
				if (FieldBean.isStringType(field)) {
					field.setSize(metaData.getColumnDisplaySize(i + 1));
				} else if (FieldBean.isNumberType(field)) {
					field.setSize(metaData.getPrecision(i + 1));
					field.setScale(metaData.getScale(i + 1));
				}
				int t = metaData.isNullable(i + 1);
				t = t == 0 ? 1 : -1;
				field.setCanNull(t);
				fields.add(field);
			}
			table.setFields(fields);
			// 获取表主键信息
			DatabaseMetaData dbmd = service.getConnection().getMetaData();
			results = dbmd.getPrimaryKeys(null, null, table.getTableName());
			if (results.next()) {
				FieldBean field = table.getFieldBeanByFieldName(results.getString(4));
				field.setPk(1);
			}
		}
		return list;
	}

	/**
	 * 功能:与现有的数据库比较，生成数据库表定义或更新SQL语句
	 * 
	 * @param table 数据库表定义
	 * @return 返回SQL语句列表
	 * @throws SQLException 数据库操作异常
	 */
	@Override
	public String getUpdateSQL(Service service, TableBean table) throws SQLException {
		// 检查数据库中是否有此表
		DatabaseBean db = BootStrap.getBootBean().getDatabaseBean(table.getDatabaseName());
		String databaseName = db.getDatabaseName();
		String tableName = table.getTableName();
		String sql = "SELECT count(*) FROM information_schema.tables WHERE table_schema='public'";
		sql = sql + " and table_catalog ='" + databaseName + "'";
		sql = sql + " and UPPER(table_name) = '" + tableName + "'";
		ResultSet results = service.getConnection().createStatement().executeQuery(sql);
		boolean flag = false;
		while (results.next()) {
			Long l = results.getLong(1);
			flag = l > 0;
		}
		results.close();
		// 如果表结构存在
		if (flag) {
			// 从数据库获取元数据
			Map<String, DataTypeBean> types = service.getDataBaseTypeMap();
			TableBean bak = new TableBean();
			// 获取视图的字段信息
			PreparedStatement pstmt;
			sql = "SELECT * FROM " + tableName;
			List<FieldBean> fields = new ArrayList<>();
			pstmt = service.getConnection().prepareStatement(sql);
			ResultSetMetaData metaData = pstmt.getMetaData();
			for (int i = 0; i < metaData.getColumnCount(); i++) {
				FieldBean field = new FieldBean();
				field.setFieldName(metaData.getColumnName(i + 1).toUpperCase());
				field.setDescription(metaData.getColumnLabel(i + 1));
				field.setDbType(metaData.getColumnTypeName(i + 1));
				DataTypeBean type = types.get(field.getDbType().toUpperCase());
				if (type == null) {
					LOG.error(field.getFieldName() + ':' + field.getDbType() + "没有对应的类型！");
					continue;
				}
				field.setType(type.getName());
				field.setJavaType(type.getJavaType());
				field.setPropertyName(field.getFieldName());
				if (FieldBean.isStringType(field)) {
					field.setSize(metaData.getColumnDisplaySize(i + 1));
				} else if (FieldBean.isNumberType(field)) {
					field.setSize(metaData.getPrecision(i + 1));
					field.setScale(metaData.getScale(i + 1));
				}
				int t = metaData.isNullable(i + 1);
				t = t == 0 ? 1 : -1;
				field.setCanNull(t);
				fields.add(field);
			}
			bak.setTableName(tableName);
			bak.setFields(fields);
			// 获取表主键信息
			DatabaseMetaData dbmd = service.getConnection().getMetaData();
			results = dbmd.getPrimaryKeys(null, null, tableName);
			if (results.next()) {
				FieldBean field = bak.getFieldBeanByFieldName(results.getString(4));
				field.setPk(1);
			}
			return this.getSQL(table, bak);
		} else {
			return this.getSQL(table);
		}
	}

	/**
	 * 功能:根据提供的数据库表描述，生成数据库表定义SQL语句
	 * 
	 * @param table 数据库表定义
	 * @return 返回SQL语句列表
	 */
	@Override
	public String getSQL(TableBean table) {
		StringBuilder sql = new StringBuilder();
		if (table.getFields() != null) {
			// 遍历每一个字段的定义，创建SQL语句
			for (FieldBean field : table.getFields()) {
				// 如果不需要持久化
				if (!field.isPresist()) {
					continue;
				}
				sql.append(field.getFieldName()).append(" ");
				sql.append(field.getDbType());
				addSize(sql, field); // 添加字段长度
				if (field.isPk()) {
					sql.append(" primary key");
				}
				if (!field.isCanNull()) {
					sql.append(" not null");
				}
				if (field.getDefaultValue() != null && !field.getDefaultValue().equals("")) {
					sql.append(" default ").append(field.getDefaultValue());
				}
				sql.append(",\n");
			}
		}
		// 如果至少有一个字段
		if (sql.length() > 0) {
			sql.insert(0, "create table " + table.getTableName() + "(\n");
			sql.deleteCharAt(sql.length() - 2);
			sql.append(")");
			return sql.toString();
		} else {
			return null;
		}
	}

	/**
	 * 功能:根据提供的数据库表描述，生成数据库表结构修改SQL语句
	 * 
	 * @param table 新的数据库表定义
	 * @param bak   旧的数据库表定义
	 * @return 返回SQL语句列表
	 */
	@Override
	public String getSQL(TableBean table, TableBean bak) {
		StringBuilder sql = new StringBuilder();
		// 将所有旧定义的表字段信息存在Map类中
		Map<String, FieldBean> map = new HashMap<>();
		if (bak.getFields() != null) {
			for (FieldBean field : bak.getFields()) {
				map.put(field.getFieldName(), field);
			}
		}
		// 遍历所有新定义的表字段数据
		for (FieldBean field : table.getFields()) {
			// 从旧表中取出与之同名的字段定义，生成SQL语句并同时删除旧表中已使用过的字段定义
			getSQL(sql, field, map.remove(field.getFieldName()), map);
		}
		// 如果需要删除未定义的数据库字段
		if (BootStrap.getBootBean().isUpdateDeleteField()) {
			// 遍历所有旧表中有，新表定义中没有的字段，生成删除字段的SQL语句
			for (FieldBean field : map.values()) {
				sql.append(" drop column ").append(field.getFieldName()).append(",\n");
			}
		}
		// 如果至少有一个字段更新
		if (sql.length() > 0) {
			sql.insert(0, "alter table " + table.getTableName() + '\n');
			sql.deleteCharAt(sql.length() - 2);
			sql.append(";");
			return sql.toString();
		} else {
			return null;
		}
	}

	/**
	 * 功能:创建数据库更新某个表结构的SQL语句
	 * <p>
	 * 如果返回空值，则不需要执行此SQL语句
	 * 
	 * @param sql   SQL语句存储类
	 * @param field 表新的字段定义
	 * @param old   表旧的字段定义
	 * @return 返回SQL语句
	 */
	private void getSQL(StringBuilder sql, FieldBean field, FieldBean old, Map<String, FieldBean> delMap) {
		// 如果旧表定义中没有这个字段，则添加这个字段定义
		if (old == null) {
			// 如果不需要持久化
			if (!field.isPresist()) {
				return;
			}
			// 更新不管主键的变更
			if (field.isPk()) {
				return;
			}
			sql.append(" add column ");
			sql.append(field.getFieldName()).append(" ");
		} else {
			// 如果新旧字段的定义完全一致，则不需要生成更新SQL语句
			if (field.getFieldName().equals(old.getFieldName()))
				return;
			// 如果不需要持久化
			if (!field.isPresist()) {
				if (!old.isPresist()) {
					return;
				} else {
					// 删除字段
					delMap.put(field.getFieldName(), field);
					return;
				}
			}
			// 如果需要持久化
			else {
				// 如果旧的不需要持久化，则新增
				if (!old.isPresist()) {
					sql.append(" add column ");
					sql.append(field.getFieldName()).append(" ");
					// 更新不管主键的变更
					if (field.isPk()) {
						sql.append(" primary key");
					}
				}
				// 如果旧的需要持久化，则修改
				else {
					// 生成更新SQL语句
					sql.append(" change column ").append(old.getFieldName()).append(' ').append(field.getFieldName());
				}
			}
		}
		sql.append(' ').append(field.getDbType());
		addSize(sql, field); // 添加字段长度
		if (!field.isCanNull()) {
			sql.append(" not null");
		}
		if (field.getDefaultValue() != null && !field.getDefaultValue().equals("")) {
			sql.append(" default ").append(field.getDefaultValue());
		}
		sql.append(",\n");
	}

	/**
	 * 功能:添加字段长度
	 * 
	 * @param sql   SQL语句拼接语句
	 * @param field 表的字段定义
	 */
	private void addSize(StringBuilder sql, FieldBean field) {
		// 如果是字符串，提供默认长度255
		if (FieldBean.isStringType(field)) {
			// 超长文本不需要指定长度
			if (!field.getType().equals("超长文本")) {
				int size = field.getSize();
				if (size == 0) {
					size = JAVA_STRING_DEFAULT_SIZE;
				}
				sql.append('(').append(size).append(") ");
			}
		}
		// 如果是数字
		else if (FieldBean.isStringType(field)) {
			int size = field.getSize();
			int scale = field.getScale();
			if (size == 0) {
				String type = field.getJavaType();
				// 如果是布尔型，默认长度是1
				if (type.equals(ConstantCode.JAVA_TYPE_BOOLEAN)) {
					size = JAVA_BOOLEAN_DEFAULT_SIZE;
				}
				// 如果是整型，默认长度是10
				else if (type.equals(ConstantCode.JAVA_TYPE_INTEGER)) {
					size = JAVA_INTEGER_DEFAULT_SIZE;
				}
				// 如果是长整型，默认长度是10
				else if (type.equals(ConstantCode.JAVA_TYPE_LONG)) {
					// size = JAVA_INTEGER_DEFAULT_SIZE;
				}
				// 如果是双精度型，默认长度是10
				else if (type.equals(ConstantCode.JAVA_TYPE_DOUBLE)) {
					// size = JAVA_INTEGER_DEFAULT_SIZE;
				}
				// 如果是大数字，默认长度是10，3
				else if (type.equals(ConstantCode.JAVA_TYPE_BIGDECIMAL)) {
					size = JAVA_DECLIMAL_DEFAULT_SIZE;
					if (scale == 0) {
						scale = JAVA_DECLIMAL_DEFAULT_SCALE;
					}
				}
			}
			if (size != 0) {
				sql.append('(').append(size);
				if (scale != 0) {
					sql.append(',').append(scale);
				}
				sql.append(") ");
			}
		}
	}

	public static int JAVA_STRING_DEFAULT_SIZE = 255; // 默认字符串长度
	public static int JAVA_BOOLEAN_DEFAULT_SIZE = 1; // 默认布尔类型的长度
	public static int JAVA_INTEGER_DEFAULT_SIZE = 10; // 默认整数长度
	public static int JAVA_DECLIMAL_DEFAULT_SIZE = 10; // 默认大数字整数部分长度
	public static int JAVA_DECLIMAL_DEFAULT_SCALE = 3; // 默认大数字小数部分长度
}
